package br.ufrj.dcc.mapaviolencia.util;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.commons.httpclient.util.HttpURLConnection;

import br.ufrj.dcc.mapaviolencia.model.Bairro;
import br.ufrj.dcc.mapaviolencia.model.Cidade;
import br.ufrj.dcc.mapaviolencia.model.Estado;
import br.ufrj.dcc.mapaviolencia.model.Logradouro;
import br.ufrj.dcc.mapaviolencia.model.Noticia;

import com.google.code.geocoder.Geocoder;
import com.google.code.geocoder.GeocoderRequestBuilder;
import com.google.code.geocoder.model.GeocodeResponse;
import com.google.code.geocoder.model.GeocoderRequest;
import com.google.code.geocoder.model.GeocoderResult;
import com.mongodb.DBCollection;

public class LocalizacaoUtil {

	public static final int DEFAULT_PATTERN_FLAG = Pattern.CASE_INSENSITIVE;
	private static List<Estado> estados; 
	public static DBCollection collection;
	
	public static void initialize() {
		
		collection = DBUtil.getInstance().getDatabase().getCollection(DBUtil.COLLECTION_LOCALIDADES);
		@SuppressWarnings("unchecked")
		List<String> estadosBanco = collection.distinct(Estado.NOME_ESTADO);
		estados = new ArrayList<Estado>();
		for(String estado : estadosBanco){
			estados.add(new Estado(estado));
		}
	}
	
	public static void findLocalidade(String textoNoticia, Noticia noticia){
		String noticiaLimpa = TextMiningUtil.specialCharClean(textoNoticia);
		Object localidade = null;
		//BUSCA TOP-DOWN 
		//Object localidade = topDownSearch(estados, noticiaLimpa);
		
		//Se retorna bairro ou logradouro está otimo, se cidade, também não procura mais, pois já não encontrou dentre os bairros
		//Se retorna estado, ainda da pra procurar mais
		if(localidade == null || localidade.getClass().equals(Estado.class)){
			Object novaLocalidade = null;

			//PULAR UM NIVEL DE BUSCA
			List<Cidade> allCidades = getAllCidades(estados);
			novaLocalidade = searchAllCidades(noticiaLimpa, allCidades);

			//PULA MAIS UM NIVEL
//			if(novaLocalidade == null){
//				//TODO:pegar apenas bairros do estado encontrado
//				
//				List<Bairro> allBairros = getAllBairros(allCidades);
//				novaLocalidade = searchAllBairros(noticiaLimpa, allBairros);//Apenas retorna caso ache bairro e rua
//			}
			
			if(novaLocalidade != null){
				localidade = novaLocalidade;
			}
			
		}
		
		if(localidade != null){
			setGeoLocalizacao(localidade,noticia);
		}
		else{
			setGeoLocalizacao(estados.get(0), noticia);
		}
	}

	private static void setGeoLocalizacao(Object localidade, Noticia noticia) {
		String geolocalizacao = "";
		if(localidade.getClass().equals(Estado.class)){
			geolocalizacao = ((Estado)localidade).getSigla();
		}else if(localidade.getClass().equals(Cidade.class)){
			Cidade cidade = (Cidade)localidade;
			geolocalizacao = cidade.getNome() + ", " + cidade.getEstado().getSigla();
		}else if(localidade.getClass().equals(Bairro.class)){
			Bairro bairro = (Bairro)localidade;
			geolocalizacao = bairro.getCidade().getNome() + ", " + bairro.getCidade().getEstado().getSigla() + " - " + bairro.getNome();
		}else if(localidade.getClass().equals(Logradouro.class)){
			Logradouro log = (Logradouro)localidade;
			geolocalizacao = log.getLogradouro() + ", " + log.getBairro().getCidade().getNome() + ", " + log.getBairro().getCidade().getEstado().getSigla() + " - " + log.getBairro().getNome();
		}
		
		Geocoder geocoder = new Geocoder();
		GeocoderRequest geocoderRequest = new GeocoderRequestBuilder().setAddress(geolocalizacao).getGeocoderRequest();
		GeocodeResponse geocoderResponse = geocoder.geocode(geocoderRequest);
		List<GeocoderResult> results = geocoderResponse.getResults();
		float latitude = results.get(0).getGeometry().getLocation().getLat().floatValue();
		float longitude = results.get(0).getGeometry().getLocation().getLng().floatValue();
		
		noticia.setLatitude(latitude);
		noticia.setLongitude(longitude);
		noticia.setGeolocalizacao(geolocalizacao);
	}

	public static Object searchAllCidades(String noticiaLimpa, List<Cidade> allCidades) {
		//TODO: pegar apenas cidades do estado encontrado			
		ArrayList<Cidade> cidadesEncontradas = searchCidade(allCidades, noticiaLimpa);
		if(cidadesEncontradas.size() > 0){
			for(Cidade cidade : cidadesEncontradas){
				ArrayList<Bairro> bairrosEncontrados = searchBairro(cidade.getBairros(), noticiaLimpa);
				if(bairrosEncontrados.size() > 0){
					Bairro bairro = bairrosEncontrados.get(0);
					Logradouro log = searchLogradouro(bairro.getLogradouros(), noticiaLimpa);
					if(log != null){
						return log;
					}
					return bairro;
				}
				else{
					//TODO:searchAllLogradouros
				}
				return cidade;
			}
		}
		return null;
	}

	private static List<Cidade> getAllCidades(List<Estado> estados) {
		ArrayList<Cidade> allCidades = new ArrayList<Cidade>();
		for(Estado estado : estados){
			allCidades.addAll(estado.getCidades());
		}
		return allCidades;
	}

	private static List<Bairro> getAllBairros(List<Cidade> allCidades) {
		ArrayList<Bairro> allBairros = new ArrayList<Bairro>();
		for(Cidade cidade : allCidades){
			allBairros.addAll(cidade.getBairros());
		}
		return allBairros;
	}

	public static Object searchAllBairros(String noticiaLimpa, List<Bairro> allBairros) {
		ArrayList<Bairro> bairrosEncontrados = searchBairro(allBairros, noticiaLimpa);
		if(bairrosEncontrados.size() > 0){
			for(Bairro bairro : bairrosEncontrados){
				Logradouro log = searchLogradouro(bairro.getLogradouros(), noticiaLimpa);
				if(log != null){
					//Apenas considera bairro encontrado caso também ache o logradouro [Bairros se repetem muito]
					return log;
				}
				
				return bairro;
			}
		}
		return null;
	}

	private static Object topDownSearch(List<Estado> estados, String noticiaLimpa){
		ArrayList<Estado> estadosEncontrados = searchEstado(estados,noticiaLimpa);
		if(estadosEncontrados.size() > 0){
			Estado estado = estadosEncontrados.get(0);
			ArrayList<Cidade> cidadesEncontradas = searchCidade(estado.getCidades(), noticiaLimpa);
			if(cidadesEncontradas.size() > 0){
				Cidade cidade = cidadesEncontradas.get(0);
				ArrayList<Bairro> bairrosEncontrados = searchBairro(cidade.getBairros(), noticiaLimpa);
				if(bairrosEncontrados.size() > 0){
					Bairro bairro = bairrosEncontrados.get(0);
					Logradouro log = searchLogradouro(bairro.getLogradouros(), noticiaLimpa);
					if(log != null){
						return log;
					}
				}else return cidade;
			}else return estado;
		}
		return null;
	}
	private static ArrayList<Estado> searchEstado(List<Estado> estados, String noticiaLimpa){
		ArrayList<Estado> foundEstados = new ArrayList<Estado>();
		for(Estado item : estados){
			if(TextMiningUtil.match(item.getNome() + " | " + item.getSigla(),noticiaLimpa)){
				foundEstados.add(item);
			}
		}
		return foundEstados;
	}
	
	private static ArrayList<Cidade> searchCidade(List<Cidade> cidades, String noticiaLimpa){
		ArrayList<Cidade> foundCidades = new ArrayList<Cidade>();
		for(Cidade item : cidades){
			if(TextMiningUtil.match(item.getNome(),noticiaLimpa)){
				foundCidades.add(item);
			}
		}
		return foundCidades;
	}
	private static ArrayList<Bairro> searchBairro(List<Bairro> bairros, String noticiaLimpa){
		ArrayList<Bairro> foundBairros = new ArrayList<Bairro>();
		for(Bairro item : bairros){
			if(TextMiningUtil.match(item.getNome(),noticiaLimpa)){
				foundBairros.add(item);
			}
		}
		return foundBairros;
	}
	private static Logradouro searchLogradouro(List<Logradouro> logradouros, String noticiaLimpa){
		for(Logradouro item : logradouros){
			if(TextMiningUtil.match(item.getLogradouro() + " | " + item.getCep(),noticiaLimpa)){
				return item;
			}
		}
		return null;
	}
}
